package com.easychat.websocket;

import com.easychat.entity.constants.Constants;
import com.easychat.entity.dto.MessageSendDTO;
import com.easychat.entity.dto.WsInitDataDTO;
import com.easychat.entity.enums.MessageTypeEnum;
import com.easychat.entity.enums.UserContactTypeEnum;
import com.easychat.entity.po.ChatMessage;
import com.easychat.entity.po.ChatSessionUser;
import com.easychat.entity.po.UserContactApply;
import com.easychat.entity.po.UserInfo;
import com.easychat.entity.query.ChatMessageQuery;
import com.easychat.entity.query.ChatSessionUserQuery;
import com.easychat.entity.query.UserContactApplyQuery;
import com.easychat.entity.query.UserInfoQuery;
import com.easychat.mappers.ChatMessageMapper;
import com.easychat.mappers.ChatSessionUserMapper;
import com.easychat.mappers.UserContactApplyMapper;
import com.easychat.mappers.UserInfoMapper;
import com.easychat.utils.JsonUtils;
import com.easychat.utils.RedisUtil;
import com.easychat.utils.StringTools;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.apache.catalina.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @Author: yudaxian
 * @Description: TODO
 * @DateTime: 2024/8/20 19:01
 **/
@Component
public class ChannelContextUtils {

    private static final ConcurrentHashMap<String, Channel> USER_CONTEXT_MAP = new ConcurrentHashMap<>();

    private static final ConcurrentHashMap<String, ChannelGroup> GROUP_CONTEXT_MAP = new ConcurrentHashMap<>();
    private static final Logger log = LoggerFactory.getLogger(ChannelContextUtils.class);

    @Resource
    private UserInfoMapper<UserInfo, UserInfoQuery> userInfoMapper;

    @Resource
    private ChatSessionUserMapper<ChatSessionUser, ChatSessionUserQuery> chatSessionUserMapper;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private ChatMessageMapper<ChatMessage, ChatMessageQuery> chatMessageMapper;

    @Resource
    private UserContactApplyMapper<UserContactApply, UserContactApplyQuery> userContactApplyMapper;

    public void addContext(String userId, Channel channel) {
        String channelId = channel.id().toString();
        AttributeKey attributeKey = null;
        if (!AttributeKey.exists(channelId)) {
            attributeKey = AttributeKey.newInstance(channelId);
        } else {
            attributeKey = AttributeKey.valueOf(channelId);
        }
        channel.attr(attributeKey).set(userId);

        List<String> contactIdList = redisTemplate.opsForList().range(Constants.REDIS_KEY_USER_CONTACT + userId, 0, -1);

        for (String contactId : contactIdList) {
            if (contactId.startsWith(UserContactTypeEnum.GROUP.getPrefix())) {
                // 如果是群组，加入到群组中
                add2Group(contactId, channel);
            }
        }

        USER_CONTEXT_MAP.put(userId, channel);
        redisUtil.saveHeartBeat(userId);

        // 更新用户最后连接时间
        UserInfo updateInfo = new UserInfo();
        updateInfo.setLastLoginTime(new Date());
        userInfoMapper.updateByUserId(updateInfo, userId);

        // 给用户发送消息
        UserInfo userInfo = userInfoMapper.selectByUserId(userId);
        // 获取最后离线时间，查询最近三天的消息，最多三天
        Long lastOffTime = userInfo.getLastOffTime();
        if (lastOffTime != null && System.currentTimeMillis() - Constants.MillionSeconds_3Days_AGO > lastOffTime) {
            lastOffTime = Constants.MillionSeconds_3Days_AGO;
        }

        // 查询用户所有的会话消息, 换设备也能查出来
        List<ChatSessionUser> chatSessionUsers = chatSessionUserMapper.selectUserChatSession(userId);

        WsInitDataDTO wsInitDataDTO = new WsInitDataDTO();
        wsInitDataDTO.setChatSessionList(chatSessionUsers);
        // 离线消息的处理
        // 查询聊天信息
        // 消息发给我的和我加入的群组聊天信息
        List<String> groupIdList = contactIdList.stream().filter(item -> item.startsWith(UserContactTypeEnum.GROUP.getPrefix())).collect(Collectors.toList());
        groupIdList.add(userId);
        List<ChatMessage> chatMessageList = this.chatMessageMapper.selectMyMessage(groupIdList, lastOffTime);
        wsInitDataDTO.setChatMessageList(chatMessageList);
        // 查询好友申请
        Integer applyCount = this.userContactApplyMapper.selectMyApply(userId, lastOffTime);
        wsInitDataDTO.setApplyCount(applyCount);

        MessageSendDTO messageSendDTO = new MessageSendDTO();
        messageSendDTO.setMessageType(MessageTypeEnum.INIT.getType());
        messageSendDTO.setContactId(userId);
        messageSendDTO.setExtendData(wsInitDataDTO);
        sendMsg(messageSendDTO, userId);
    }

    public void sendMsg(MessageSendDTO messageSendDTO, String receiveId) {
        if (receiveId == null) {
            return;
        }
        Channel sendChannel = USER_CONTEXT_MAP.get(receiveId);
        if (sendChannel == null) {
            return;
        }
        // 相对于客户端而言，联系人就是发送人，这里转换再发送
        if (MessageTypeEnum.ADD_FRIEND_SELF.getType().equals(messageSendDTO.getMessageType())) {
            UserInfo userInfo = (UserInfo) messageSendDTO.getExtendData();
            messageSendDTO.setMessageType(MessageTypeEnum.ADD_FRIEND.getType());
            messageSendDTO.setContactId(userInfo.getUserId());
            messageSendDTO.setContactName(userInfo.getNickName());
            messageSendDTO.setExtendData(null);
        } else {
            messageSendDTO.setContactId(messageSendDTO.getSendUserId());
            messageSendDTO.setContactName(messageSendDTO.getSendUserNickName());
        }

        sendChannel.writeAndFlush(new TextWebSocketFrame(JsonUtils.convertJson2Json(messageSendDTO)));

    }

    public void addUser2Group(String userId, String groupId) {
        Channel channel = USER_CONTEXT_MAP.get(userId);
        add2Group(groupId, channel);
    }

    private void add2Group(String groupId, Channel context) {
        ChannelGroup group = GROUP_CONTEXT_MAP.get(groupId);
        if (group == null) {
            // 没有就创建
            group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
            GROUP_CONTEXT_MAP.put(groupId, group);
        }
        if (context == null) {
            return;
        }
        group.add(context);
    }

    public void removeContext(Channel channel, String userId) {
        if (!StringTools.isEmpty(userId)) {
            USER_CONTEXT_MAP.remove(userId);
        }
        redisUtil.removeUserHeartBeat(userId);

        // 更新用户最后离线时间，只要断开就更新用户离线时间
        UserInfo userInfo = new UserInfo();
        userInfo.setLastLoginTime(new Date());
        userInfoMapper.updateByUserId(userInfo, userId);
    }

    public void sendMessage(MessageSendDTO messageSendDTO) {
        UserContactTypeEnum typeEnum = UserContactTypeEnum.getByPrefix(messageSendDTO.getContactId());
        switch (typeEnum) {
            case USER:
                this.send2User(messageSendDTO);
                break;
            case GROUP:
                this.send2Group(messageSendDTO);
                break;
        }
    }

    private void send2User(MessageSendDTO messageSendDTO) {
        String contactId = messageSendDTO.getContactId();
        sendMsg(messageSendDTO, contactId);
        // 强制下线消息
        if (MessageTypeEnum.FORCE_OFF_LINE.getType().equals(messageSendDTO.getMessageType())) {
            closeContext(contactId);
        }
    }

    public void closeContext(String userId) {
        if (StringTools.isEmpty(userId)) {
            return;
        }
        redisUtil.clearUserTokenByUserId(userId);
        Channel channel = USER_CONTEXT_MAP.get(userId);
        if (channel == null) {
            return;
        }
        channel.close();
    }

    private void send2Group(MessageSendDTO messageSendDTO) {
        if (StringTools.isEmpty(messageSendDTO.getContactId())) {
            return;
        }
        ChannelGroup channelGroup = GROUP_CONTEXT_MAP.get(messageSendDTO.getContactId());
        if (channelGroup == null) {
            return;
        }
        channelGroup.writeAndFlush(new TextWebSocketFrame(JsonUtils.convertJson2Json(messageSendDTO)));

        // 移除群聊
        MessageTypeEnum messageType = MessageTypeEnum.getByType(messageSendDTO.getMessageType());
        if (messageType == MessageTypeEnum.LEAVE_GROUP || messageType == MessageTypeEnum.REMOVE_GROUP) {
            String userId = (String) messageSendDTO.getExtendData();
            redisTemplate.opsForList().remove(Constants.REDIS_KEY_USER_CONTACT + userId, 0, messageSendDTO.getContactId());
            Channel channel = USER_CONTEXT_MAP.get(userId);
            if (channel == null) {
                return;
            }
            channelGroup.remove(channel);
        }
        if (messageType == MessageTypeEnum.DISSOLUTION_GROUP) {
            GROUP_CONTEXT_MAP.remove(messageSendDTO.getContactId());
            channelGroup.close();
        }
    }
}
